#include <node_api.h>
#include <malloc.h>
#include <functional>
#include <memory>
#include <bitset>
#include <string>
#define NAPI_EXPERIMENTAL

#ifndef __GlobalRecorderHandler_deftag
#define __GlobalRecorderHandler_deftag
namespace GlobalRecorderHandler
{
    napi_value (*start)(napi_env env, napi_callback_info info);
    typedef struct
    {
        napi_async_work work;
        napi_threadsafe_function tsfn;
    } Addon;
    napi_env env;
    Addon *addon;
    void catch_err(napi_env env, napi_status status)
    {

        if (status != napi_ok)
        {

            const napi_extended_error_info *error_info;
            napi_get_last_error_info(env, &error_info);

            const char *error_message = error_info->error_message != nullptr ? error_info->error_message : "Unknown error";
            napi_throw_error(env, nullptr, error_message);
        }
    }
    struct CallbackArgs
    {
        int type, action, value, x, y;
        CallbackArgs(int type, int action, int value, int x, int y)
        {
            this->type = type;
            this->action = action;
            this->value = value;
            this->x = x;
            this->y = y;
        }
        napi_value *toArgs()
        {
            napi_value *args = (napi_value *)malloc(sizeof(napi_value) * 5);
            napi_create_int32(env, type, args + 0);
            napi_create_int32(env, action, args + 1);
            napi_create_int32(env, value, args + 2);
            napi_create_int32(env, x, args + 3);
            napi_create_int32(env, y, args + 4);
            return args;
        }
    };
    struct KeyState
    {
        bool isDown, isToggled;
        KeyState()
        {
            this->isDown = false;
            this->isToggled = false;
        }
        KeyState(bool isDown, bool isToggled)
        {
            this->isDown = isDown;
            this->isToggled = isToggled;
        }

        static napi_value toObj(napi_env env, std::unique_ptr<KeyState> keyState)
        {
            napi_value obj;
            napi_create_object(env, &obj);
            napi_value isDown, isToggled;
            napi_get_boolean(env, keyState->isDown, &isDown);
            napi_get_boolean(env, keyState->isToggled, &isToggled);
            napi_set_named_property(env, obj, "isDown", isDown);
            napi_set_named_property(env, obj, "isToggled", isToggled);
            return obj;
        }
    };
    static void call_js(napi_env env, napi_value js_cb, void *context, void *data)
    {
        GlobalRecorderHandler::CallbackArgs *argsFromCaller = (GlobalRecorderHandler::CallbackArgs *)data;
        if (env != NULL)
        {
            napi_value undefined;
            napi_value *args = argsFromCaller->toArgs();
            napi_get_undefined(env, &undefined);
            GlobalRecorderHandler::catch_err(env, napi_call_function(env,
                                                                     undefined, // The `this` object of js callback
                                                                     js_cb,
                                                                     5,      // Number of arguments
                                                                     args,   // Arguments
                                                                     NULL)); // Ignoring return values
            free(data);
            free(args);
        }
    }

    struct ModuleLocalInfo
    {
        std::function<napi_value(napi_env, napi_callback_info)> setEventListener;
        std::function<napi_value(napi_env, napi_callback_info)> state;
        std::function<void(napi_env env, napi_status status, void *data)> work_complete;
        std::function<void(napi_env env, void *data)> execute_work;
        std::string type;
        ModuleLocalInfo()
        {

            state = nullptr;
            work_complete = nullptr;
            execute_work = nullptr;
            type = "unknown";
        }
        ModuleLocalInfo(
            std::function<napi_value(napi_env, napi_callback_info)> state,
            std::function<void(napi_env env, napi_status status, void *data)> work_complete,
            std::function<void(napi_env env, void *data)> execute_work,
            std::string type)
        {

            this->state = state;
            this->work_complete = work_complete;
            this->execute_work = execute_work;
            this->type = type;
        }
        ModuleLocalInfo(ModuleLocalInfo &other)
        {

            this->state = other.state;
            this->work_complete = other.work_complete;
            this->execute_work = other.execute_work;
            this->type = other.type;
        }
        ModuleLocalInfo &operator=(ModuleLocalInfo &other)
        {

            this->state = other.state;
            this->work_complete = other.work_complete;
            this->execute_work = other.execute_work;
            this->type = other.type;
            return *this;
        }
        ModuleLocalInfo(ModuleLocalInfo &&other)
        {

            this->state = other.state;
            this->work_complete = other.work_complete;
            this->execute_work = other.execute_work;
            this->type = other.type;

            other.state = nullptr;
            other.work_complete = nullptr;
            other.execute_work = nullptr;
            other.type = "unknown";
        }
    } localInfo;
    napi_value setEventListener(napi_env env, napi_callback_info info)
    {
        size_t argc = 1;                     // The number of arguments passed by js
        napi_value js_cb;                    // The First argument(js-callback)
        napi_value work_name;                // Thread name
        GlobalRecorderHandler::Addon *addon; // “实例化” 结构体 (个人理解是取出了传进来的 js-cabllback 地址指针，期待大神来讨论 ?)
        napi_status sts;                     // 程序执行状态

        sts = napi_get_cb_info(
            env,              // 执行上下文，可以理解为 js 那个 “事件环”
            info,             // 上下文信息
            &argc,            // 收到参数的个数
            &js_cb,           // 接收 js 参数
            NULL,             // 接收 js 的 this 对象
            (void **)(&addon) // 取得 js 传进来的 callback 的指针地址
        );
        GlobalRecorderHandler::catch_err(env, sts);

        GlobalRecorderHandler::catch_err(env, napi_create_string_utf8(env,
                                                                      "N-API Thread-safe Call from Async Work Item",
                                                                      NAPI_AUTO_LENGTH,
                                                                      &work_name));

        sts = napi_create_threadsafe_function(env,

                                              // 其他线程的 js 函数
                                              // call_js 的第二个参数
                                              // 也就是我们 addon.start(function) 传进来的 function
                                              js_cb,

                                              // 可能传递给一些异步任务async_hooks钩子传递初始化数据 (期待大神讨论 ?)
                                              // 个人理解 N-API 中的 async 指的就是多线程任务
                                              // 一个线程任务，在 N-API 中由 async work 调用
                                              NULL,

                                              work_name,       // 给线程起个名字，给 async_hooks 钩子提供一个标识符
                                              0,               // (官网直译)最大线程队列数量，0 代表没限制
                                              1,               // (官网直译)初始化线程数量，其中包括主线程
                                              NULL,            // (官网直译)线程之间可以传递数据(官网直译)
                                              NULL,            // (官网直译)线程之间可以传递函数，函数注销时候被调用
                                              NULL,            // (官网直译)附加给函数的执行上下文，应该就是
                                              call_js,         // call_js 的第三个参数
                                              &(addon->tsfn)); // js 传进来的函数，可以理解为真实的 js 函数所在内存地址 (期待大神讨论 ?)
        GlobalRecorderHandler::catch_err(env, sts);

        // 负责执行上面创建的函数
        sts = napi_create_async_work(env,
                                     NULL,      // 可能传递 async_hooks 一些初始化数据
                                     work_name, // 给线程起个名字，给 async_hooks 钩子提供一个标识符
                                     [](napi_env env, void *data) -> void
                                     { localInfo.execute_work(env, data); }, // 线程执行时候执行的函数 (与主线程并行执行)
                                     [](napi_env env, napi_status status, void *data) -> void
                                     { localInfo.work_complete(env, status, data); }, // 线程执行完时候的回调
                                     addon,                                           // 既 execute_work、work_complete 中的 void* data
                                     &(addon->work));                                 // 线程句柄
        GlobalRecorderHandler::catch_err(env, sts);

        // 将线程放到待执行队列中
        sts = napi_queue_async_work(env,
                                    // 要执行线程的句柄
                                    addon->work);
        GlobalRecorderHandler::catch_err(env, sts);

        return NULL; // 这个貌似是返回给 js-callback 的返回值
    };

    napi_value createModule(napi_env now_env, napi_value exports, ModuleLocalInfo _localInfo)
    {

        // 这里等价于 const obj = new Object();
        // 这回知道面向对象是咋来的了吧 ?
        // 类的本质就是“结构体”演化而来的，new(开辟堆内存空间) 关键字是 malloc(申请内存空间) 变种过来的
        Addon *addon = (Addon *)malloc(sizeof(*addon));
        localInfo = _localInfo;

        // 等价于 obj.work = null;
        addon->work = NULL;

        // 个人 js 水平有限，Object 类研究的不深
        // 可以说，精通 Object 类的小伙伴可以自己想想咯，反正给对象挂一个属性、函数需要的东东，都在这里了
        // 相等于 const fun = () => {}, attr = 'Hello';
        napi_property_descriptor _setEventListener = {
            "setEventListener", // 属性名称
            NULL,               // -- 没想明白
            setEventListener,   // 函数体
            NULL,               // 属性 getter
            NULL,               // 属性 setter
            NULL,               // 属性描述符
            napi_default,
            addon // (官网直译)也可以写 NULL，调用 getter 时候返回的数据
        };
        napi_property_descriptor _state = {
            "state",
            NULL,
            [](napi_env env, napi_callback_info info) -> napi_value
            { return localInfo.state(env, info); },
            NULL,
            NULL,
            NULL,
            napi_default,
            NULL};
        napi_property_descriptor _apiType = {
            "apiType",
            NULL,
            NULL,
            [](napi_env env, napi_callback_info info) -> napi_value
            {
                napi_value apiType;
                napi_create_string_utf8(env, localInfo.type.c_str(), NAPI_AUTO_LENGTH, &apiType);
                return apiType;
            },
            NULL,
            NULL,
            napi_default,
            NULL};
        // 相当于 const obj = { fun, attr };
        napi_define_properties(now_env, exports, 1, &_setEventListener); // 将属性挂载到 exports 上面
        napi_define_properties(now_env, exports, 1, &_state);            // 将属性挂载到 exports 上面
        napi_define_properties(now_env, exports, 1, &_apiType);          // 将属性挂载到 exports 上面
        env = now_env;
        return exports;
    }

}

#endif // __GlobalRecorderHandler_deftag